# @Time : 2024-09-30 17:18
# @Author : Fioman 
# @Phone : 13149920693
"""
主要用来和生产作业相关的控制逻辑写到这里,简写为ph
"""
import queue
import threading
import time
from datetime import datetime

import gevent

from app.device_state import DeviceStates
from app.send_to_ui_handler import *
from common import file_dir_helper
from common.datetime_helper import get_datetime_with_hms
from common.enum_model import RoleType, ProdErrorType, RotatedType
from common.produce_logger import ProduceLogger
from db_tools.db_handler import DbHandler
from tools.line_camera_params import LineCameraParams

db = DbHandler()
logger = ProduceLogger()
device_states = DeviceStates()
cp = LineCameraParams()


class ProduceHandler(object):
    def __init__(self, image_joint, cutter_cam_cal, offline_zmc, cam_zmc, cbj_zmc, cutter_zmc):
        self.start_produce_last_click = 0  # 最后一次点击自动作业循环
        self.cutter_zmc = cutter_zmc
        self.cbj_zmc = cbj_zmc
        self.cam_zmc = cam_zmc
        self.offline_zmc = offline_zmc
        self.cutter_cam_cal = cutter_cam_cal
        self.image_joint = image_joint
        self.reaload_working_info()  # 加载生产的信息

    @staticmethod
    def login_from_web(user_info):
        """
        界面上请求登录界面
        :param user_info:用户信息,包含用户名和密码
        :return:
        """
        username = user_info.get("userName", "")
        password = user_info.get("password", "")

        if username == 'admin' and password == 'hdno.1':
            res = {
                'currentAuthority': 'admin',
                'status': 'ok',
                'type': 'account'
            }
            device_states.is_login = True
            return res
        result = db.get_userinfo_by_name(username)
        userinfo_get = result.data
        if not result.state:
            res = {
                'status': 'error',
                'errorMsg': '你输入的用户名不存在,请重新输入'
            }
            return res
        elif username == "" and password == "":
            res = {
                'status': 'error',
                'errorMsg': '输入的用户名和密码不能是空,请重新输入'
            }
            return res
        elif username != userinfo_get.get("username", "") or password != userinfo_get.get("password", ""):
            res = {
                'currentAuthority': 'guest',
                'status': 'error',
                'errorMsg': '你输入的密码错误,请重新输入',
                'type': 'account'
            }
            return res
        else:
            if userinfo_get.get("role", "") == RoleType.普通人员.name:
                res = {
                    'currentAuthority': "user",
                    'status': "ok",
                    'type': "account"
                }
            else:
                res = {
                    'currentAuthority': "serviceman",
                    'status': "ok",
                    'type': "account"
                }

            # 登录成功,刷新登录时间.
            query = {
                "username": username
            }
            new_values = {
                "last_login_time": get_datetime_with_hms()
            }
            db.update_userinfo(query, new_values)
            device_states.is_login = True
            return res

    def save_order_from_web(self, order_info):
        """
        界面上点击了保存工单
        """
        save_key = order_info["key"]

    def reload_working_order(self, order_info, is_first_board=True):
        """
        加载作业的工单信息
        """
        self.is_first_board = is_first_board
        self.order_key = order_info["key"]
        self.order_id = order_info["order_id"]
        self.order_width_mm = order_info["width"]
        self.order_height_mm = order_info["height"]
        calc_width = order_info["calc_width"]
        calc_height = order_info["calc_height"]
        if calc_width and calc_height:
            self.width_mm = calc_width
            self.height_mm = calc_height
        else:
            self.width_mm = self.order_width_mm
            self.height_mm = self.order_height_mm
        self.width_pix = self.width_mm * cp.mm2pix
        self.height_pix = self.height_mm * cp.mm2pix
        self.top_edge_mm = order_info["top_edge"]
        self.left_edge_mm = order_info["left_edge"]
        self.bottom_edge_mm = order_info["bottom_edge"]
        self.right_edge_mm = order_info["right_edge"]
        self.top_edge_pix = int(self.top_edge_mm * cp.mm2pix)
        self.left_edge_pix = int(self.left_edge_mm * cp.mm2pix)
        self.bottom_edge_pix = (self.bottom_edge_mm * cp.mm2pix)
        self.right_edge_mm = int(self.right_edge_mm * cp.mm2pix)
        self.is_break = True if order_info["is_break"] == "是" else False
        self.layout_mode = order_info["layout"]
        if self.layout_mode == "竖排":
            self.rotated_type = RotatedType.NoRotated
        else:
            self.rotated_type = RotatedType.ClockWise
        self.amount = order_info["amount"]
        self.joint_number = order_info["joint_number"]
        result = db.get_cut_speed_by_cropper_thick(order_info["cropper_thickness"])
        if result.state:
            self.first_cut_speed = result.data.get("first_cut_speed")
            self.cut_speed = result.data.get("cut_speed")
        else:
            logger.debug(result.info)
            self.first_cut_speed = 800
            self.cut_speed = 900
        result = db.get_knife_cut_prams()
        if not result.state:
            logger.debug(result.info)
        else:
            self.cut_deep = result.data.get("cut_deep")
        # 如果生产工单的key和上一个工单的key不同,就证明更换了工单,工单上的数量就重置我0
        self.amount = 0
        # 然后就是初始化工单图像目录
        file_dir_helper.create_image_dir_by_order_id(self.order_id, self.joint_number)
        # 更新working_order信息
        new_values = {
            "order_used_key": self.order_key,
            "order_used_id": self.order_id,
            "cut_speed": self.cut_speed,
            "cut_deep": self.cut_deep
        }
        self.update_working_info(new_values)
        if self.joint_number == "2":
            result = self.cutter_zmc.set_two_board_joint_flag()
            if not result.state:
                self.deal_prod_error(result.info, ProdErrorType.camZmcError)
                return
        elif self.joint_number == "1":
            result = self.cutter_zmc.set_single_board_joint_flag()
            if not result.state:
                self.deal_prod_error(result.info, ProdErrorType.camZmcError)
                return
        elif self.joint_number == "3":
            result = self.cutter_zmc.set_three_board_joint_flag()
            if not result.state:
                self.deal_prod_error(result.info, ProdErrorType.camZmcError)
                return
        else:
            logger.debug("目前只设置了1,2,3拼的流程,设置的拼层不符合要求,请重新设定.")

    def update_working_info(self, new_values):
        """
        更新生产信息,每次更新的时候,都重新加载一次
        """
        result = db.update_working_info(new_values)
        if not result.state:
            logger.debug(result.info)
        else:
            self.reaload_working_info()

    def reaload_working_info(self):
        """
        重新加载working_info的信息
        """
        result = db.get_working_info()
        if not result.state:
            logger.debug(result.info)
            return
        working_info = result.data
        # 计算台布使用时间
        working_info["table_used"] = (datetime.now() - datetime.strptime(
            working_info["table_start_day"], "%Y-%m-%d")).days
        send_working_info_to_ui(working_info)
        self.working_info_key = working_info["order_used_key"]
        self.working_info_order_id = working_info["order_used_id"]
        self.work_index = int(working_info["work_index"])
        self.cut_areas = float(working_info["cut_areas"])
        self.cut_speed = float(working_info["cut_speed"])
        self.cut_deep = float(working_info["cut_deep"])
        self.cut_amount = int(working_info["cut_amount"])
        self.index_max = int(working_info["index_max"])
        self.knife_max = int(working_info["knife_max"])
        self.knife_used = float(working_info["knife_used"])
        self.cam_zmc_index = self.work_index
        self.cbj_zmc_index = self.work_index
        self.cutter_zmc_index = self.work_index

    @staticmethod
    def deal_prod_error(info, error_type):
        if error_type == ProdErrorType.camZmcError:
            error_msg = f"拍照控制器通信异常: {info}"
        elif error_type == ProdErrorType.cbjZmcError:
            error_msg = f"裁切机控制器通信异常: {info}"
        elif error_type == ProdErrorType.cutterZmcError:
            error_msg = f"铡刀控制器通信异常: {info}"
        elif error_type == ProdErrorType.cameraError:
            error_msg = f"相机获取图像异常: {info}"
        elif error_type == ProdErrorType.cutLineQueueError:
            error_msg = f"切线队列异常,清线重新开始自动作业: {info}"
        else:
            error_msg = info
        logger.debug(error_msg)
        send_prod_error_to_ui(error_msg)

    def start_produce_from_web(self, order_info):
        """
        前端界面点击了自动作业循环
        @param order_info:
        @return:
        """
        if self.start_produce_last_click == 0:
            self.start_produce_last_click = time.time()
        else:
            current_click_time = time.time() - self.start_produce_last_click
            if current_click_time - self.start_produce_last_click < 2:
                logger.debug("开始按钮2秒内点击了两次,判定为重复点击,本次不做响应.")
                return
            else:
                self.start_produce_last_click = current_click_time

        # 1. 自动作业状态初始化
        self.reload_working_order(order_info, is_first_board=True)
        # 2. 自动作业参数加载
        self.reload_prod_params()
        logger.debug(f"自动作业开启,作业参数加载完成:\n"
                     f"工单Id:{self.order_id},width:{self.order_width_mm},height:{self.order_height_mm},"
                     f"拼版方式:{self.layout_mode},{self.joint_number}拼板,易断板{self.is_break}\n,"
                     f"下刀深度:{self.cut_deep},切割速度:{self.cut_speed},X轴极限:{self.cbj_limit_x},"
                     f"Y轴极限:{self.cbj_limit_y}")
        # 3. 启动设备
        result = self.cutter_zmc.cutter_zmc_start()
        if not result.state:
            self.deal_prod_error(result.info, ProdErrorType.cutterZmcError)
            return result.info
        start_info = "设备启动中..."
        send_start_state_to_ui(start_info)
        gevent.sleep(0.5)
        self.produce_start_check = True
        while self.produce_start_check:
            result01 = self.cam_zmc.is_running()
            result02 = self.cbj_zmc.is_running()
            result03 = self.cutter_zmc.is_running()
            if not result01.state or not result02.state or not result03.state:
                self.deal_prod_error(result01.info, ProdErrorType.cutterZmcError)
            info01 = "1. 上料启动中..." if result01.data == 0 else "1. 上料启动成功."
            info02 = "2. 裁切机启动中..." if result02.data == 0 else "2. 裁切机启动成功."
            info03 = "3. 铡刀启动中..." if result03.data == 0 else "3. 铡刀启动成功."
            if result01.data == 1 and result02.data == 1 and result03.data == 1:
                self.produce_start_check = False
            else:
                start_info = info01 + " " + info02 + " " + info03
                send_start_state_to_ui(start_info)
                gevent.sleep(0.1)
        start_info = ""
        send_start_state_to_ui(start_info)
        device_states.stop_work_flag = False
        self.cam_zmc_task_thread = threading.Thread(target=self.cam_zmc_task, name="cam_zmc_th")
        self.cam_zmc_task_thread.setDaemon(True)
        self.cam_zmc_task_thread.start()

        self.cbj_zmc_task_thread = threading.Thread(target=self.cbj_zmc_task, name="cbj_zmc_th")
        self.cbj_zmc_task_thread.setDaemon(True)
        self.cbj_zmc_task_thread.start()

        self.cutter_zmc_task = threading.Thread(target=self.cutter_zmc_task, name="cutter_zmc_th")
        self.cutter_zmc_task.setDaemon(True)
        self.cutter_zmc_task.start()
        return "ok"

    def reload_prod_params(self):
        """
        加载生产中的一些参数,包括矩阵,还有阈值
        @return:
        """
        app_config.websocket_q = queue.Queue()
        self.reload_cutter_cam_check_thres()
        self.is_allow_get_image = True
        self.wait_board_back = False
        self.reload_trans_mtx()
        self.reload_cbj_axis_limit()

    def reload_cutter_cam_check_thres(self):
        """
        重新加载铡刀阈值相关的参数
        @return:
        """
        result = db.get_cutter_cam_check_thres()
        if result.state:
            data = result.data
            self.begin_angle_thres = data.get("begin_angle_thres")
            self.begin_angle_offset = data.get("begin_angle_offset")
            self.move_offset = data.get("move_offset")
            self.final_first_board_thres = data.get("final_first_board_thres")
            self.final_second_board_thres = data.get("final_second_board_thres")
            self.final_total_board_thres = data.get("final_total_board_thres")
            self.final_front_angle_thres = data.get("final_front_angle_thres")
            self.final_back_angle_thres = data.get("final_back_angle_thres")
            self.final_fold_thres = data.get("final_fold_thres")
        else:
            logger.debug(result.info)

    def reload_trans_mtx(self):
        """
        重新加载标定矩阵
        @return:
        """
        result = db.get_trans_mtx()
        if result.state:
            data = result.data
            self.mtx = data.get("mtx")
            self.mtx_rotate = data.get("mtx_rotate")
        else:
            logger.debug(result.info)

    def reload_cbj_axis_limit(self):
        """
        重新加载裁切机参数,主要是x轴和y轴的极限位置
        @return:
        """
        result = db.get_cbj_zmc_params()
        if result.state:
            data = result.data
            self.cbj_limit_x = data.get("fx_max")
            self.cbj_limit_y = data.get("fy_max")
        else:
            logger.debug(result.info)

    def cam_zmc_task(self):
        """
        上料监听任务开启,主要负责拍照以及和底层通信
        @return:
        """
        logger.debug("1. 上料拍照任务开启.")
        image_failed_count = 0
        self.first_check_board_len01 = 0
        self.first_check_board_len02 = 0
        while True:
            if device_states.stop_work_flag:
                logger.debug(f"id_{self.cam_zmc_index} stop_work_flag = True,上料拍照任务退出")
                return
            if not self.is_allow_get_image:
                gevent.sleep(0.5)
                continue
            if self.wait_board_back:
                gevent.sleep(0.5)
                continue
            result = self.cam_zmc.get_allow_get_pic_state()
            if not result.state:
                self.deal_prod_error(result.info, ProdErrorType.camZmcError)
                return
            if result.data == 1:
                logger.debug(f"id_{self.cam_zmc_index} 上料: get modbusbit(15) = 1,收到拍照信号!")
                self.line_image = None
                result = self.image_joint.get_image_from_line_cam()
                if not result.state:
                    if image_failed_count == 0:
                        logger.debug(f"id_{self.cam_zmc_index} 上料: 相机获取图片失败1次,"
                                     f"重新打开相机并进行重拍,失败原因: {result.info}")
                        result = self.cam_zmc.img_result_failed()
                        if not result.state:
                            self.deal_prod_error(result.info, ProdErrorType.camZmcError)
                            return
                        self.image_joint.re_start_line_camera()
                        image_failed_count += 1
                        continue
                    else:
                        logger.debug(f"id_{self.cam_zmc_index} 上料: 相机获取失败2次,弹窗提示: {result.info}")
                        image_failed_count = 0
                        self.deal_prod_error(result.info, ProdErrorType.cameraError)
                        continue
                else:
                    self.lineImage = result.data
                    logger.debug(f"id_{self.cam_zmc_index} 上料: 相机获取图片OK")
                    image_failed_count = 0


            else:
                gevent.sleep(0.1)
                continue
